home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1993 July / InfoMagic USENET CD-ROM July 1993.ISO / sources / misc / volume17 / strftime / part01 next >
Encoding:
Internet Message Format  |  1991-03-02  |  14.3 KB

  1. From: arnold@audiofax.com (Arnold Robbins)
  2. Newsgroups: comp.sources.misc
  3. Subject: v17i030:  strftime - Implementation of ANSI strftime routine, Part01/01
  4. Message-ID: <1991Mar2.002827.16523@sparky.IMD.Sterling.COM>
  5. Date: 2 Mar 91 00:28:27 GMT
  6. Approved: kent@sparky.imd.sterling.com
  7. X-Checksum-Snefru: c93190be c701a697 81550969 9aacf3dc
  8.  
  9. Submitted-by: Arnold Robbins <arnold@audiofax.com>
  10. Posting-number: Volume 17, Issue 30
  11. Archive-name: strftime/part01
  12. Supersedes: strftime: Volume 16, Issue 94
  13.  
  14. Here is a new and improved version of the strftime routine and man page
  15. posted a few weeks ago.  It's not worth posting diffs, the whole package
  16. is pretty small.
  17.  
  18. Enjoy,
  19.  
  20. Arnold Robbins                AudioFAX, Inc. | Laundry increases
  21. 2000 Powers Ferry Road, #200 / Marietta, GA. 30067     | exponentially in the
  22. INTERNET: arnold@audiofax.com Phone:   +1 404 933 7612 | number of children.
  23. UUCP:      emory!audfax!arnold Fax-box: +1 404 618 4581 |   -- Miriam Robbins
  24. ------------------------------ cut here -------------------------------
  25. #! /bin/sh
  26. # This is a shell archive, meaning:
  27. # 1. Remove everything above the #! /bin/sh line.
  28. # 2. Save the resulting text in a file.
  29. # 3. Execute the file with /bin/sh (not csh) to create:
  30. #    strftime.c
  31. #    strftime.3
  32. # This archive created: Fri Mar  1 11:49:00 1991
  33. export PATH; PATH=/bin:/usr/bin:$PATH
  34. echo shar: "extracting 'strftime.c'" '(6976 characters)'
  35. if test -f 'strftime.c'
  36. then
  37.     echo shar: "will not over-write existing file 'strftime.c'"
  38. else
  39. cat << \SHAR_EOF > 'strftime.c'
  40. /*
  41.  * strftime.c
  42.  *
  43.  * Public-domain relatively quick-and-dirty implemenation of
  44.  * ANSI library routine for System V Unix systems.
  45.  *
  46.  * It's written in old-style C for maximal portability.
  47.  * However, since I'm used to prototypes, I've included them too.
  48.  *
  49.  * If you want stuff in the System V ascftime routine, add the SYSV_EXT define.
  50.  *
  51.  * The code for %c, %x, and %X is my best guess as to what's "appropriate".
  52.  * This version ignores LOCALE information.
  53.  * It also doesn't worry about multi-byte characters.
  54.  * So there.
  55.  *
  56.  * Arnold Robbins
  57.  * January, February, 1991
  58.  *
  59.  * Fixes from ado@elsie.nci.nih.gov
  60.  * February 1991
  61.  */
  62.  
  63. #include <stdio.h>
  64. #include <string.h>
  65. #include <time.h>
  66. #include <sys/types.h>
  67.  
  68. #ifndef __STDC__
  69. #define const    /**/
  70. #endif
  71.  
  72. #ifndef __STDC__
  73. extern void tzset();
  74. extern char *strchr();
  75. static int weeknumber();
  76. #else
  77. extern void tzset(void);
  78. extern char *strchr(const char *str, int ch);
  79. static int weeknumber(const struct tm *timeptr, int firstweekday);
  80. #endif
  81.  
  82. extern char *tzname[2];
  83. extern int daylight;
  84.  
  85. #define SYSV_EXT    1    /* stuff in System V ascftime routine */
  86.  
  87. /* strftime --- produce formatted time */
  88.  
  89. #ifndef __STDC__
  90. size_t
  91. strftime(s, maxsize, format, timeptr)
  92. char *s;
  93. size_t maxsize;
  94. const char *format;
  95. const struct tm *timeptr;
  96. #else
  97. size_t
  98. strftime(char *s, size_t maxsize, const char *format, const struct tm *timeptr)
  99. #endif
  100. {
  101.     char *endp = s + maxsize;
  102.     char *start = s;
  103.     char tbuf[100];
  104.     int i;
  105.     static short first = 1;
  106.  
  107.     /* various tables, useful in North America */
  108.     static char *days_a[] = {
  109.         "Sun", "Mon", "Tue", "Wed",
  110.         "Thu", "Fri", "Sat",
  111.     };
  112.     static char *days_l[] = {
  113.         "Sunday", "Monday", "Tuesday", "Wednesday",
  114.         "Thursday", "Friday", "Saturday",
  115.     };
  116.     static char *months_a[] = {
  117.         "Jan", "Feb", "Mar", "Apr", "May", "Jun",
  118.         "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
  119.     };
  120.     static char *months_l[] = {
  121.         "January", "February", "March", "April",
  122.         "May", "June", "July", "August", "September",
  123.         "October", "November", "December",
  124.     };
  125.     static char *ampm[] = { "AM", "PM", };
  126.  
  127.     if (s == NULL || format == NULL || timeptr == NULL || maxsize == 0)
  128.         return 0;
  129.  
  130.     if (strchr(format, '%') == NULL && strlen(format) + 1 >= maxsize)
  131.         return 0;
  132.  
  133.     if (first) {
  134.         tzset();
  135.         first = 0;
  136.     }
  137.  
  138.     for (; *format && s < endp - 1; format++) {
  139.         tbuf[0] = '\0';
  140.         if (*format != '%') {
  141.             *s++ = *format;
  142.             continue;
  143.         }
  144.         switch (*++format) {
  145.         case '\0':
  146.             *s++ = '%';
  147.             goto out;
  148.  
  149.         case '%':
  150.             *s++ = '%';
  151.             continue;
  152.  
  153.         case 'a':    /* abbreviated weekday name */
  154.             if (timeptr->tm_wday < 0 || timeptr->tm_wday > 6)
  155.                 strcpy(tbuf, "?");
  156.             else
  157.                 strcpy(tbuf, days_a[timeptr->tm_wday]);
  158.             break;
  159.  
  160.         case 'A':    /* full weekday name */
  161.             if (timeptr->tm_wday < 0 || timeptr->tm_wday > 6)
  162.                 strcpy(tbuf, "?");
  163.             else
  164.                 strcpy(tbuf, days_l[timeptr->tm_wday]);
  165.             break;
  166.  
  167. #ifdef SYSV_EXT
  168.         case 'h':    /* abbreviated month name */
  169. #endif
  170.         case 'b':    /* abbreviated month name */
  171.             if (timeptr->tm_mon < 0 || timeptr->tm_mon > 11)
  172.                 strcpy(tbuf, "?");
  173.             else
  174.                 strcpy(tbuf, months_a[timeptr->tm_mon]);
  175.             break;
  176.  
  177.         case 'B':    /* full month name */
  178.             if (timeptr->tm_mon < 0 || timeptr->tm_mon > 11)
  179.                 strcpy(tbuf, "?");
  180.             else
  181.                 strcpy(tbuf, months_l[timeptr->tm_mon]);
  182.             break;
  183.  
  184.         case 'c':    /* appropriate date and time representation */
  185.             sprintf(tbuf, "%s %s %2d %02d:%02d:%02d %d",
  186.                 days_a[timeptr->tm_wday],
  187.                 months_a[timeptr->tm_mon],
  188.                 timeptr->tm_mday,
  189.                 timeptr->tm_hour,
  190.                 timeptr->tm_min,
  191.                 timeptr->tm_sec,
  192.                 timeptr->tm_year + 1900);
  193.             break;
  194.  
  195.         case 'd':    /* day of the month, 01 - 31 */
  196.             sprintf(tbuf, "%02d", timeptr->tm_mday);
  197.             break;
  198.  
  199.         case 'H':    /* hour, 24-hour clock, 00 - 23 */
  200.             sprintf(tbuf, "%02d", timeptr->tm_hour);
  201.             break;
  202.  
  203.         case 'I':    /* hour, 12-hour clock, 01 - 12 */
  204.             i = timeptr->tm_hour;
  205.             if (i == 0)
  206.                 i = 12;
  207.             else if (i > 12)
  208.                 i -= 12;
  209.             sprintf(tbuf, "%02d", i);
  210.             break;
  211.  
  212.         case 'j':    /* day of the year, 001 - 366 */
  213.             sprintf(tbuf, "%03d", timeptr->tm_yday + 1);
  214.             break;
  215.  
  216.         case 'm':    /* month, 01 - 12 */
  217.             sprintf(tbuf, "%02d", timeptr->tm_mon + 1);
  218.             break;
  219.  
  220.         case 'M':    /* minute, 00 - 59 */
  221.             sprintf(tbuf, "%02d", timeptr->tm_min);
  222.             break;
  223.  
  224.         case 'p':    /* am or pm based on 12-hour clock */
  225.             if (timeptr->tm_hour < 12)
  226.                 strcpy(tbuf, ampm[0]);
  227.             else
  228.                 strcpy(tbuf, ampm[1]);
  229.             break;
  230.  
  231.         case 'S':    /* second, 00 - 61 */
  232.             sprintf(tbuf, "%02d", timeptr->tm_sec);
  233.             break;
  234.  
  235.         case 'U':    /* week of year, Sunday is first day of week */
  236.             sprintf(tbuf, "%d", weeknumber(timeptr, 0));
  237.             break;
  238.  
  239.         case 'w':    /* weekday, Sunday == 0, 0 - 6 */
  240.             sprintf(tbuf, "%d", timeptr->tm_wday);
  241.             break;
  242.  
  243.         case 'W':    /* week of year, Monday is first day of week */
  244.             sprintf(tbuf, "%d", weeknumber(timeptr, 1));
  245.             break;
  246.  
  247.         case 'x':    /* appropriate date representation */
  248.             sprintf(tbuf, "%s %s %2d %d",
  249.                 days_a[timeptr->tm_wday],
  250.                 months_a[timeptr->tm_mon],
  251.                 timeptr->tm_mday,
  252.                 timeptr->tm_year + 1900);
  253.             break;
  254.  
  255.         case 'X':    /* appropriate time representation */
  256.             sprintf(tbuf, "%02d:%02d:%02d",
  257.                 timeptr->tm_hour,
  258.                 timeptr->tm_min,
  259.                 timeptr->tm_sec);
  260.             break;
  261.  
  262.         case 'y':    /* year without a century, 00 - 99 */
  263.             i = timeptr->tm_year % 100;
  264.             sprintf(tbuf, "%d", i);
  265.             break;
  266.  
  267.         case 'Y':    /* year with century */
  268.             sprintf(tbuf, "%d", 1900 + timeptr->tm_year);
  269.             break;
  270.  
  271.         case 'Z':    /* time zone name or abbrevation */
  272.             i = 0;
  273.             if (daylight && timeptr->tm_isdst)
  274.                 i = 1;
  275.             strcpy(tbuf, tzname[i]);
  276.             break;
  277.  
  278. #ifdef SYSV_EXT
  279.         case 'n':    /* same as \n */
  280.             tbuf[0] = '\n';
  281.             tbuf[1] = '\0';
  282.             break;
  283.  
  284.         case 't':    /* same as \t */
  285.             tbuf[0] = '\t';
  286.             tbuf[1] = '\0';
  287.             break;
  288.  
  289.         case 'D':    /* date as %m/%d/%y */
  290.             strftime(tbuf, sizeof tbuf, "%m/%d/%y", timeptr);
  291.             break;
  292.  
  293.         case 'e':    /* day of month, blank padded */
  294.             sprintf(tbuf, "%2d", timeptr->tm_mday);
  295.             break;
  296.  
  297.         case 'r':    /* time as %I:%M:%S %p */
  298.             strftime(tbuf, sizeof tbuf, "%I:%M:%S %p", timeptr);
  299.             break;
  300.  
  301.         case 'R':    /* time as %H:%M */
  302.             strftime(tbuf, sizeof tbuf, "%H:%M", timeptr);
  303.             break;
  304.  
  305.         case 'T':    /* time as %H:%M:%S */
  306.             strftime(tbuf, sizeof tbuf, "%H:%M:%S", timeptr);
  307.             break;
  308. #endif
  309.  
  310.         default:
  311.             tbuf[0] = '%';
  312.             tbuf[1] = *format;
  313.             tbuf[2] = '\0';
  314.             break;
  315.         }
  316.         i = strlen(tbuf);
  317.         if (i)
  318.             if (s + i < endp - 1) {
  319.                 strcpy(s, tbuf);
  320.                 s += i;
  321.             } else
  322.                 return 0;
  323.     }
  324. out:
  325.     if (s < endp && *format == '\0') {
  326.         *s = '\0';
  327.         return (s - start);
  328.     } else
  329.         return 0;
  330. }
  331.  
  332. /* weeknumber --- figure how many weeks into the year */
  333.  
  334. /* With thanks and tip of the hatlo to ado@elsie.nci.nih.gov */
  335.  
  336. #ifndef __STDC__
  337. static int
  338. weeknumber(timeptr, firstweekday)
  339. const struct tm *timeptr;
  340. int firstweekday;
  341. #else
  342. static int
  343. weeknumber(const struct tm *timeptr, int firstweekday)
  344. #endif
  345. {
  346.     if (firstweekday == 0)
  347.         return (timeptr->tm_yday + 7 - timeptr->tm_wday) / 7;
  348.     else
  349.         return (timeptr->tm_yday + 7 -
  350.             (timeptr->tm_wday ? (timeptr->tm_wday - 1) : 6)) / 7;
  351. }
  352. SHAR_EOF
  353. fi
  354. echo shar: "extracting 'strftime.3'" '(5331 characters)'
  355. if test -f 'strftime.3'
  356. then
  357.     echo shar: "will not over-write existing file 'strftime.3'"
  358. else
  359. cat << \SHAR_EOF > 'strftime.3'
  360. .TH STRFTIME 3
  361. .SH NAME
  362. strftime \- generate formatted time information
  363. .SH SYNOPSIS
  364. .ft B
  365. .nf
  366. #include <sys/types.h>
  367. #include <time.h>
  368. .sp
  369. size_t strftime(char *s, size_t maxsize, const char *format,
  370.     const struct tm *timeptr);
  371. .SH DESCRIPTION
  372. The following description is transcribed verbatim from the December 7, 1988
  373. draft standard for ANSI C.
  374. This draft is essentially identical in technical content
  375. to the final version of the standard.
  376. .LP
  377. The
  378. .B strftime
  379. function places characters into the array pointed to by
  380. .B s
  381. as controlled by the string pointed to by
  382. .BR format .
  383. The format shall be a multibyte character sequence, beginning and ending in
  384. its initial shift state.
  385. The
  386. .B format
  387. string consists of zero or more conversion specifiers and ordinary
  388. multibyte characters.  A conversion specifier consists of a
  389. .B %
  390. character followed by a character that determines the behavior of the
  391. conversion specifier.
  392. All ordinary multibyte characters (including the terminating null
  393. character) are copied unchanged into the array.
  394. If copying takes place between objects that overlap the behavior is undefined.
  395. No more than
  396. .B maxsize
  397. characters are placed into the array.
  398. Each conversion specifier is replaced by appropriate characters as described
  399. in the following list.
  400. The appropriate characters are determined by the
  401. .B LC_TIME
  402. category of the current locale and by the values contained in the
  403. structure pointed to by
  404. .BR timeptr .
  405. .TP
  406. .B %a
  407. is replaced by the locale's abbreviated weekday name.
  408. .TP
  409. .B %A
  410. is replaced by the locale's full weekday name.
  411. .TP
  412. .B %b
  413. is replaced by the locale's abbreviated month name.
  414. .TP
  415. .B %B
  416. is replaced by the locale's full month name.
  417. .TP
  418. .B %c
  419. is replaced by the locale's appropriate date and time representation.
  420. .TP
  421. .B %d
  422. is replaced by the day of the month as a decimal number
  423. .RB ( 01 - 31 ).
  424. .TP
  425. .B %H
  426. is replaced by the hour (24-hour clock) as a decimal number
  427. .RB ( 00 - 23 ).
  428. .TP
  429. .B %I
  430. is replaced by the hour (12-hour clock) as a decimal number
  431. .RB ( 01 - 12 ).
  432. .TP
  433. .B %j
  434. is replaced by the day of the year as a decimal number
  435. .RB ( 001 - 366 ).
  436. .TP
  437. .B %m
  438. is replaced by the month as a decimal number
  439. .RB ( 01 - 12 ).
  440. .TP
  441. .B %M
  442. is replaced by the minute as a decimal number
  443. .RB ( 00 - 59 ).
  444. .TP
  445. .B %p
  446. is replaced by the locale's equivalent of the AM/PM designations associated
  447. with a 12-hour clock.
  448. .TP
  449. .B %S
  450. is replaced by the second as a decimal number
  451. .RB ( 00 - 61 ).
  452. .TP
  453. .B %U
  454. is replaced by the week number of the year (the first Sunday as the first
  455. day of week 1) as a decimal number
  456. .RB ( 00 - 53 ).
  457. .TP
  458. .B %w
  459. is replaced by the weekday as a decimal number
  460. .RB [ "0 " (Sunday)- 6 ].
  461. .TP
  462. .B %W
  463. is replaced by the week number of the year (the first Monday as the first
  464. day of week 1) as a decimal number
  465. .RB ( 00 - 53 ).
  466. .TP
  467. .B %x
  468. is replaced by the locale's appropriate date representation.
  469. .TP
  470. .B %X
  471. is replaced by the locale's appropriate time representation.
  472. .TP
  473. .B %y
  474. is replaced by the year without century as a decimal number
  475. .RB ( 00 - 99 ).
  476. .TP
  477. .B %Y
  478. is replaced by the year with century as a decimal number.
  479. .TP
  480. .B %Z
  481. is replaced by the time zone name or abbreviation, or by no characters if
  482. no time zone is determinable.
  483. .TP
  484. .B %%
  485. is replaced by
  486. .BR % .
  487. .LP
  488. If a conversion specifier is not one of the above, the behavior is
  489. undefined.
  490. .SH RETURNS
  491. If the total number of resulting characters including the terminating null
  492. character is not more than
  493. .BR maxsize ,
  494. the
  495. .B strftime
  496. function returns the number of characters placed into the array pointed to
  497. by
  498. .B s
  499. not including the terminating null character.
  500. Otherwise, zero is returned and the contents of the array are indeterminate.
  501. .SH NON-ANSI EXTENSIONS
  502. If
  503. .B SYSV_EXT
  504. is defined when the routine is compiled, then the following additional
  505. conversions will be available.
  506. These are borrowed from the System V
  507. .IR cftime (3)
  508. and
  509. .IR ascftime (3)
  510. routines.
  511. .TP
  512. .B %D
  513. is equivalent to specifying
  514. .BR %m/%d/%y .
  515. .TP
  516. .B %e
  517. is replaced by the day of the month,
  518. padded with a blank if it is only one digit.
  519. .TP
  520. .B %h
  521. is equivalent to
  522. .BR %b ,
  523. above.
  524. .TP
  525. .B %n
  526. is replaced with a newline character (\s-1ASCII LF\s+1).
  527. .TP
  528. .B %r
  529. is equivalent to specifying
  530. .BR "%I:%M:%S %p" .
  531. .TP
  532. .B %R
  533. is equivalent to specifying
  534. .BR %H:%M: .
  535. .TP
  536. .B %T
  537. is equivalent to specifying
  538. .BR %H:%M:%S .
  539. .TP
  540. .B %t
  541. is replaced with a \s-1TAB\s+1 character.
  542. .SH SEE ALSO
  543. time(2), ctime(3), localtime(3)
  544. .SH BUGS
  545. This version does not handle multibyte characters or pay attention to the
  546. setting of the
  547. .B LC_TIME
  548. environment variable.
  549. .LP
  550. It is not clear what is ``appropriate'' for the C locale; the values
  551. returned are a best guess on the author's part.
  552. .SH CAVEATS
  553. This implementation calls
  554. .IR tzset (3)
  555. exactly once.  If the
  556. .B TZ
  557. environment variable is changed after
  558. .B strftime
  559. has been called, then
  560. .IR tzset (3)
  561. must be called again, explicitly, in order for the
  562. correct timezone information to be available.
  563. .SH AUTHOR
  564. .nf
  565. Arnold Robbins
  566. AudioFAX, Inc.
  567. Suite 200
  568. 2000 Powers Ferry Road
  569. Marietta, GA. 30067
  570. U.S.A.
  571. INTERNET: arnold@audiofax.com
  572. UUCP:     emory!audfax!arnold
  573. Phone:    +1 404 933 7600
  574. Fax-box:  +1 404 618 4581
  575. .fi
  576. .SH ACKNOWLEDGEMENTS
  577. Thanks to Geoff Clare <gwc@root.co.uk> for helping debug earlier
  578. versions of this routine.
  579. Additional thanks to Arthur David Olsen <ado@elsie.nci.nih.gov>
  580. for some code improvements.
  581. SHAR_EOF
  582. fi
  583. exit 0
  584. #    End of shell archive
  585. -- 
  586. Arnold Robbins                AudioFAX, Inc. | Laundry increases
  587. 2000 Powers Ferry Road, #200 / Marietta, GA. 30067     | exponentially in the
  588. INTERNET: arnold@audiofax.com Phone:   +1 404 933 7612 | number of children.
  589. UUCP:      emory!audfax!arnold Fax-box: +1 404 618 4581 |   -- Miriam Robbins
  590.  
  591. exit 0 # Just in case...
  592. -- 
  593. Kent Landfield                   INTERNET: kent@sparky.IMD.Sterling.COM
  594. Sterling Software, IMD           UUCP:     uunet!sparky!kent
  595. Phone:    (402) 291-8300         FAX:      (402) 291-4362
  596. Please send comp.sources.misc-related mail to kent@uunet.uu.net.
  597.